<?php
declare (strict_types = 1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
use app\service\TopviewApiService;

class CheckVoiceTasks extends Command
{
    protected function configure()
    {
        $this->setName('check:voice-tasks')
            ->setDescription('检查声音相关任务（克隆与文字转语音）状态');
    }

    protected function execute(Input $input, Output $output)
    {
        $output->writeln('开始检查声音相关任务状态...');

        $apiService = new TopviewApiService();
        $checkedClone = 0;
        $checkedTts = 0;

        try {
            // 声音克隆任务：处理中的
            $cloneTasks = Db::name('voice_clone_tasks')
                ->where('status', 1)
                ->where('task_id', '<>', '')
                ->select();

            foreach ($cloneTasks as $task) {
                try {
                    $output->writeln("检查声音克隆任务: {$task['task_id']}");
                    $result = $apiService->queryVoiceCloneTask($task['task_id']);

                    if (!isset($result['status'])) {
                        throw new \Exception('API返回结果格式错误');
                    }

                    $status = $result['status'];
                    if ($status === 'success') {
                        Db::name('voice_clone_tasks')
                            ->where('id', $task['id'])
                            ->update([
                                'status' => 2,
                                'voice_id' => $result['voice']['voiceId'] ?? $result['voice']['voiceId'] ?? null,
                                'update_time' => date('Y-m-d H:i:s')
                            ]);
                        $output->writeln("声音克隆任务 {$task['task_id']} 已完成");
                        $checkedClone++;
                    } elseif ($status === 'fail') {
                        Db::startTrans();
                        try {
                            // 更新任务状态为失败
                            Db::name('voice_clone_tasks')
                                ->where('id', $task['id'])
                                ->update([
                                    'status' => 3,
                                    'error_msg' => $result['errorMsg'] ?? '克隆失败',
                                    'update_time' => date('Y-m-d H:i:s')
                                ]);
                            
                            // 返还用户积分
                            $pointsService = new \app\service\PointsService();
                            $pointsService->recharge(
                                $task['user_id'], 
                                $task['points'],
                                '',
                                "声音克隆任务{$task['task_id']}失败退还积分"
                            );
                            
                            Db::commit();
                            $output->writeln("声音克隆任务 {$task['task_id']} 失败: " . ($result['errorMsg'] ?? '未知错误') . "，已退还{$task['points_cost']}积分");
                        } catch (\Exception $e) {
                            Db::rollback();
                            $output->writeln("声音克隆任务 {$task['task_id']} 失败且积分退还失败: " . $e->getMessage());
                        }
                        $checkedClone++;
                    }
                } catch (\Exception $e) {
                    $output->writeln("检查声音克隆任务 {$task['task_id']} 出错: " . $e->getMessage());
                }
            }

            // 文字转语音任务：处理中的
            $ttsTasks = Db::name('text2voice_tasks')
                ->where('status', 1)
                ->where('task_id', '<>', '')
                ->select();

            foreach ($ttsTasks as $task) {
                try {
                    $output->writeln("检查TTS任务: {$task['task_id']}");
                    $result = $apiService->queryText2VoiceTask($task['task_id']);

                    if (!isset($result['status'])) {
                        throw new \Exception('API返回结果格式错误');
                    }

                    $status = $result['status'];
                    if ($status === 'success') {
                        $audioUrl = $result['voice']['demoAudioUrl'] ?? '';

                        // 可选：转存到OSS
                        $ossAudioUrl = '';
                        if ($audioUrl) {
                            try {
                                $ossService = new \app\service\OssService();
                                $audioContent = $this->downloadFileWithRetry($audioUrl, 10);
                                if ($audioContent !== false) {
                                    $objectName = 'voice/text2voice/' . date('Ymd') . '/' . uniqid() . '.mp3';
                                    $tempFile = tempnam(sys_get_temp_dir(), 'tts_');
                                    file_put_contents($tempFile, $audioContent);
                                    try {
                                        $ossAudioUrl = $ossService->putObject($objectName, $tempFile);
                                    } finally {
                                        @unlink($tempFile);
                                    }
                                }
                            } catch (\Exception $e) {
                                // 忽略转存失败，保留原地址
                                \think\facade\Log::error('转存音频失败 - method: downloadFileWithRetry, url: ' . $audioUrl . ', error: ' . $e->getMessage());
                            }
                        }

                        Db::name('text2voice_tasks')
                            ->where('id', $task['id'])
                            ->update([
                                'status' => 2,
                                'audio_url' => $ossAudioUrl ?: ($audioUrl ?? ''),
                                'update_time' => date('Y-m-d H:i:s')
                            ]);
                        $output->writeln("TTS任务 {$task['task_id']} 已完成");
                        $checkedTts++;
                    } elseif ($status === 'fail') {
                        Db::startTrans();
                        try {
                            // 更新任务状态为失败
                            Db::name('text2voice_tasks')
                                ->where('id', $task['id'])
                                ->update([
                                    'status' => 3,
                                    'error_msg' => $result['errorMsg'] ?? '合成失败',
                                    'update_time' => date('Y-m-d H:i:s')
                                ]);
                            
                            // 返还用户积分
                            $pointsService = new \app\service\PointsService();
                            $pointsService->recharge(
                                $task['user_id'], 
                                $task['points'],
                                '',
                                "文本转语音任务{$task['task_id']}失败退还积分"
                            );
                            
                            Db::commit();
                            $output->writeln("文本转语音任务 {$task['task_id']} 失败: " . ($result['errorMsg'] ?? '未知错误') . "，已退还{$task['points']}积分");
                        } catch (\Exception $e) {
                            Db::rollback();
                            $output->writeln("文本转语音任务 {$task['task_id']} 失败且积分退还失败: " . $e->getMessage());
                        }
                        $checkedTts++;
                    }
                } catch (\Exception $e) {
                    $output->writeln("检查TTS任务 {$task['task_id']} 出错: " . $e->getMessage());
                }
            }

            $output->writeln("检查完成！声音克隆任务: {$checkedClone} 个，文字转语音任务: {$checkedTts} 个");
        } catch (\Exception $e) {
            $output->writeln('检查任务时发生错误: ' . $e->getMessage());
            return 1;
        }

        return 0;
    }

    // 已移除表存在性判断，按约定环境直接查询

    /**
     * 使用cURL下载文件（带重试机制）
     * @param string $url 文件URL
     * @param int $maxRetries 最大重试次数
     * @return string|false 文件内容或false
     */
    private function downloadFileWithRetry($url, $maxRetries = 10)
    {
        $lastException = null;
        
        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            try {
                return $this->downloadFile($url);
            } catch (\Exception $e) {
                $lastException = $e;
                \think\facade\Log::warning("下载文件失败 (尝试 {$attempt}/{$maxRetries}) - URL: {$url}, 错误: " . $e->getMessage());
                
                // 如果不是最后一次尝试，等待一段时间后重试
                // if ($attempt < $maxRetries) {
                    // $waitTime = pow(2, $attempt - 1); // 指数退避：1秒, 2秒, 4秒
                    // \think\facade\Log::info("等待 {$waitTime} 秒后重试...");
                    // sleep($waitTime);
                // }
            }
        }
        
        // 所有重试都失败了
        throw new \Exception("下载失败，已重试 {$maxRetries} 次。最后错误: " . $lastException->getMessage());
    }

    /**
     * 使用cURL下载文件
     * @param string $url 文件URL
     * @return string|false 文件内容或false
     */
    private function downloadFile($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过SSL验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30秒超时
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10秒连接超时
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Accept: audio/mpeg,audio/*,*/*;q=0.8',
            'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8',
            'Cache-Control: no-cache',
        ]);
        
        $content = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            throw new \Exception("cURL错误: {$error}");
        }
        
        if ($httpCode !== 200) {
            throw new \Exception("HTTP错误: {$httpCode}");
        }
        
        if ($content === false) {
            throw new \Exception("下载失败");
        }
        
        // 检查内容是否为空
        if (empty($content)) {
            throw new \Exception("下载的内容为空");
        }
        
        return $content;
    }
}


